package com.example.ass7.sprite;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

import biuoop.DrawSurface;

import com.example.ass7.collision.Collidable;
import com.example.ass7.collision.HitListener;
import com.example.ass7.collision.HitNotifier;
import com.example.ass7.dataloader.Background;
import com.example.ass7.geometryprimitives.Point;
import com.example.ass7.geometryprimitives.Rectangle;
import com.example.ass7.geometryprimitives.Velocity;
import com.example.ass7.level.GameLevel;

/**
 * @author Meirav Ben Izhak
 */
public class Block implements Collidable, Sprite, HitNotifier {

    private Rectangle rectangle;
    private List<Background> fillColors;
    private final Background defaultFillColors;
    private Background strokeColor;
    private int hitPoints;
    private int originalHitPoints;
    private final List<HitListener> hitListeners;
    private boolean alien = Boolean.FALSE;

    /**
     * @param p1
     *            p1
     * @param width
     *            width
     * @param height
     *            height
     * @param defaultFillColors
     *            color
     */
    public Block(
        Point p1, double width, double height, Background defaultFillColors) {
        this.defaultFillColors = defaultFillColors;
        this.rectangle = new Rectangle(p1, width, height);
        // this.fillColors = fillColors;
        this.hitPoints = 1;
        this.originalHitPoints = 1;
        this.hitListeners = new ArrayList<HitListener>();
    }

    /**
     * @return boolean
     */
    public boolean isAlien() {
        return alien;
    }

    /**
     * @param alien1 alien1
     */
    public void setAlien(boolean alien1) {
        this.alien = alien1;
    }

    /**
     * @param point point
     */
    public void setPosition(Point point) {
        this.rectangle = new Rectangle(point,
                rectangle.getWidth(), rectangle.getHeight());
    }

    /**
     * @return Point
     */
    public Point getPosition() {
        return this.rectangle.getUpperLeft();
    }

    /**
     * @return boolean
     */
    public boolean overRightBorder() {
        if (rectangle.getxHighest() >= 800) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    /**
     * @return boolean
     */
    public boolean overLeftBorder() {
        if (rectangle.getxLowest() <= 0) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    // Add hl as a listener to hit events.
    @Override
    public void addHitListener(HitListener hl) {
        this.hitListeners.add(hl);
    }

    // Remove hl from the list of listeners to hit events.
    @Override
    public void removeHitListener(HitListener hl) {
        this.hitListeners.remove(hl);
    }

    @Override
    public Rectangle getCollisionRectangle() {
        Rectangle rectangleToReturn = this.rectangle;
        return rectangleToReturn;
    }

    /**
     * @return @return
     */
    public int getHitPoints() {
        return this.hitPoints;
    }

    /**
     * @param numToSet
     *            numToSet
     */
    public void setHitPoints(int numToSet) {
        this.hitPoints = numToSet;
    }

    /**
     * @return Color
     */
    public Background getStrokeColor() {
        return this.strokeColor;
    }

    /**
     * @param pStrokeColor
     *            strokeColor
     */
    public void setStrokeColor(Background pStrokeColor) {
        this.strokeColor = pStrokeColor;
    }

    /**
     * @return List<Color>
     */
    public List<Background> getFillColors() {
        return this.fillColors;
    }

    /**
     * @param pFillColors
     *            fillColors
     */
    public void setFillColors(List<Background> pFillColors) {
        this.fillColors = pFillColors;
    }

    /**
     * @return Block
     */
    public Block getBlock() {
        return this;
    }

    /**
     * @param trajectoryEnd
     *            trajectoryEnd
     * @return boolean
     */
    public boolean ballHitsUpOrDown(Point trajectoryEnd) {
        double ballNextY = trajectoryEnd.getY();
        return (ballNextY >= this.rectangle.getyLowest()
               && ballNextY <= this.rectangle
                .getyHighest());
    }

    /**
     * @param trajectoryEnd
     *            trajectoryEnd
     * @return boolean
     */
    public boolean ballHitsLeftOrRight(Point trajectoryEnd) {
        double ballNextX = trajectoryEnd.getX();
        return (ballNextX >= this.rectangle.getxLowest()
                && ballNextX <= this.rectangle
                .getxHighest());
    }

    /**
     * @return Point
     */
    public Point getCenterTopPosition() {
        Point point = new Point(
                rectangle.getxLowest() + (rectangle.getWidth() / 2),
                rectangle.getyHighest() + 1);
        return point;
    }

    /**
     * @param pCollisionPoint
     *            collisionPoint
     * @return String
     */
    public String findWhichRibWillBeHit(Point pCollisionPoint) {
        String toReturn = new String();
        if (this.rectangle.getUp().isIntersectionInLine(pCollisionPoint)) {
            toReturn = "UP";
        }
        if (this.rectangle.getDown().isIntersectionInLine(pCollisionPoint)) {
            toReturn = "DOWN";
        }
        if (this.rectangle.getLeft().isIntersectionInLine(pCollisionPoint)) {
            toReturn = "LEFT";
        }
        if (this.rectangle.getRight().isIntersectionInLine(pCollisionPoint)) {
            toReturn = "RIGHT";
        }
        return toReturn;
    }

    @Override
    public Velocity hit(Ball hitter,
            Point pCollisionPoint,
            Velocity currentVelocity) {
        String ribToBeHit = this.findWhichRibWillBeHit(pCollisionPoint);
        Velocity hitToReturn = currentVelocity;
        if (ribToBeHit.equals("UP")
                || ribToBeHit.equals("DOWN")) {
            hitToReturn = new Velocity(currentVelocity.getDx(),
                    -currentVelocity.getDy());
        } else if (ribToBeHit.equals("LEFT")
                || ribToBeHit.equals("RIGHT")) {
            hitToReturn = new Velocity(-currentVelocity.getDx(),
                    currentVelocity.getDy());

        }
        if (hitToReturn != currentVelocity) {
            this.setHitPoints(this.hitPoints - 1);
        }
        this.notifyHit(hitter);
        return hitToReturn;
    }

    @Override
    public void drawOn(DrawSurface surface) {
        // gets sizes and converts it to int
        int x1 = (int) (this.rectangle.getUpperLeft().getX());
        int y1 = (int) (this.rectangle.getUpperLeft().getY());
        int width = (int) (this.rectangle.getWidth());
        int height = (int) (this.rectangle.getHeight());
        // draws the shape:
        // surface.setColor(this.fillColors.get(0));
        if (this.getFillColor().getImage() != null) {
            surface.drawImage(x1,
                    y1,
                    this.getFillColor().getImage());
        } else {
            surface.setColor(this.getFillColor().getColor());
            surface.fillRectangle(x1,
                    y1,
                    width,
                    height);
        }
//        surface.setColor(Color.black);
//        surface.drawRectangle(x1,
//                y1,
//                width,
//                height);
    }

    /**
     * @return Color
     */
    private Background getFillColor() {
        if (this.fillColors == null
                || this.fillColors.isEmpty()) {
            return this.getDefaultFillColor();
        }

        Background color = null;
        if (this.fillColors.size() > this.hitPoints - 1
                && this.hitPoints > 0) {
            color = this.fillColors.get(this.hitPoints - 1);
        }
        if (color == null) {
            return this.getDefaultFillColor();
        }
        return color;
    }

    /**
     * @return Color
     */
    private Background getDefaultFillColor() {
        if (this.defaultFillColors != null) {
            return this.defaultFillColors;
        } else {
            return new Background(null, Color.black);
        }
    }

    /**
     * @param pOriginalHitPoints
     *            originalHitPoints
     */
    public void setOriginalHitPoints(int pOriginalHitPoints) {
        this.originalHitPoints = pOriginalHitPoints;
    }

    /**
     * @return int
     */
    public int getOriginalHitPoints() {
        return this.originalHitPoints;
    }

    /**
     * @param i
     *            number of hits left.
     * @return String that should be written on the block.
     */
    public String returnHitNum(int i) {
        String toReturn;
        if (i <= 0) {
            toReturn = "X";
        } else {
            toReturn = Integer.toString(i);
        }
        return toReturn;
    }

    @Override
    public void timePassed(double dt) {
        // no process
    }

    /**
     * @param game
     *            game
     */
    public void addToGame(GameLevel game) {
        game.addSprite(this);
        game.addCollidable(this);
    }

    /**
     * @param game
     *            game
     */
    public void removeFromGame(GameLevel game) {
        game.removeSprite(this);
        game.removeCollidable(this);
    }

    /**
     * @param hitter
     *            hitter
     */
    private void notifyHit(Ball hitter) {
        // Make a copy of the hitListeners before iterating over them.
        List<HitListener> listeners = new ArrayList<HitListener>(
                this.hitListeners);
        // Notify all listeners about a hit event:
        for (HitListener hl : listeners) {
            hl.hitEvent(this, null,
                    hitter);
        }
    }

}
